/////////////////////////////////////////////////////
// File Name: frame_process_v1.v
// Author: zeping fan
// mail:   zpfan007@163.com
// Created Time: 2023年06月26日 星期一 20时50分11秒
//////////////////////////////////////////////////////

module frame_process(

clk,
rst_n,
//frame_mux interface
frame_mux_ptr_fifo_empty,
frame_mux_ptr_fifo_rd,
frame_mux_ptr_fifo_dout,
frame_mux_data_fifo_rd,
frame_mux_data_fifo_dout,

//hash_lut interface
hash_lut_source,
hash_lut_req,
hash_lut_mac,
hash_lut_hash,
hash_lut_portmap,
hash_lut_ack,
hash_lut_nak,
hash_lut_result,

//queue_controller interface
queue_process_bp,
queue_sof,
queue_dv,
queue_dout

);

parameter   MAC_HEADER_LEN = 5'd14;


input           clk;
input           rst_n;

input           frame_mux_ptr_fifo_empty;
output          frame_mux_ptr_fifo_rd;
input   [15:0]  frame_mux_ptr_fifo_dout;
output          frame_mux_data_fifo_rd;
input   [7:0]   frame_mux_data_fifo_dout;

output          hash_lut_source;
output          hash_lut_req;
output  [47:0]  hash_lut_mac;
output  [9:0]   hash_lut_hash;
output  [15:0]  hash_lut_portmap;
input           hash_lut_ack;
input           hash_lut_nak;
input   [15:0]  hash_lut_result;

input           queue_process_bp;
output          queue_sof;
output          queue_dv;
output  [7:0]   queue_dout;

wire            frame_mux_ptr_fifo_rd;
reg             frame_mux_data_fifo_rd;

reg             queue_sof;
reg             queue_dv;
reg     [7:0]   queue_dout;


localparam  IDLE        =   5'b00001;
localparam  RD_HEADER   =   5'b00010;
localparam  LEARN       =   5'b00100;
localparam  SEARCH      =   5'b01000;
localparam  TRANS       =   5'b10000;

//fsm state
reg     [4:0]   cur_state;
reg     [4:0]   nxt_state;

reg     [10:0]  frame_pyld_cnt;         //数据帧长度计数
reg     [3:0]   frame_header_cnt;       //数据帧mac地址及类型操作计数
reg             trans_mac_header_over;  //frame_header_cnt清零标志位

//frame decode
wire    [3:0]   dst_portid;
wire    [10:0]  dst_frame_len;
//frame info    
reg     [47:0]  src_mac;                //源MAC地址
reg     [47:0]  dst_mac;                //目的MAC地址
reg     [15:0]  frame_type;             //数据帧类型
reg     [3:0]   queue_portmap;          //输出给后级队列的端口映射位图(本设计中仅使用4个port)

wire            broadcast;              //广播指示寄存器
wire    [10:0]  frame_length;
//=============================FSM==============================
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)
        cur_state[4:0] <= IDLE;
    else
        cur_state[4:0] <= nxt_state[4:0];
end

always @(*)begin
    case(cur_state[4:0])
        IDLE:       nxt_state[4:0]  =   !(frame_mux_ptr_fifo_empty | queue_process_bp) ? RD_HEADER : IDLE; 
        RD_HEADER:  nxt_state[4:0]  =   (frame_header_cnt[3:0]==MAC_HEADER_LEN-1) ? LEARN : RD_HEADER;
        LEARN:      nxt_state[4:0]  =   (hash_lut_ack | hash_lut_nak) ? broadcast ? TRANS : SEARCH : LEARN;
        SEARCH:     nxt_state[4:0]  =   (hash_lut_ack | hash_lut_nak) ? TRANS : SEARCH;
        TRANS:      nxt_state[4:0]  =   (frame_pyld_cnt[10:0]==11'b0) ? IDLE : TRANS;
        default:    nxt_state[4:0]  =   IDLE;
    endcase
end
//===========================frame decode=========================
assign dst_portid[3:0] = frame_mux_ptr_fifo_dout[14:11];

assign dst_frame_len[10:0] = frame_mux_ptr_fifo_dout[10:0];
//==============================cnt===============================
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        frame_pyld_cnt[10:0]    <= 11'b0;
        frame_header_cnt[3:0]   <= 4'b0;
    end
    else if(!queue_process_bp)begin
        case(cur_state[4:0])
            IDLE:begin
                        frame_pyld_cnt[10:0]  <=  dst_frame_len[10:0] - MAC_HEADER_LEN;
                        frame_header_cnt[3:0] <=  4'b0;
            end
            RD_HEADER:
                        frame_header_cnt[3:0] <=  frame_header_cnt[3:0] + 1'b1;
            TRANS:begin
                        frame_pyld_cnt[10:0]  <= (frame_header_cnt[3:0]==MAC_HEADER_LEN+2'd1) ? frame_pyld_cnt[10:0]-1'b1 : frame_pyld_cnt[10:0];
                        frame_header_cnt[3:0] <= (frame_header_cnt[3:0] < (MAC_HEADER_LEN+2'd1) ) ? frame_header_cnt[3:0] + 1'b1 : frame_header_cnt[3:0];
            end
            default:begin
                        frame_pyld_cnt[10:0]  <= frame_pyld_cnt[10:0];
                        frame_header_cnt[3:0] <= 4'b0;
            end
        endcase
    end
end

assign frame_over = (cur_state[4:0]==TRANS) & (frame_pyld_cnt[10:0]==11'b0);

//========================frame_mux fifo rd========================
assign frame_mux_ptr_fifo_rd = frame_over;

assign frame_mux_data_fifo_rd = (cur_state[4:0]==RD_HEADER) | ((cur_state[4:0]==TRANS) & trans_mac_header_over);
//===========================addr register=======================
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)
        dst_mac[47:0] <= 48'b0;
    else if( (cur_state[4:0]==RD_HEADER) & (frame_header_cnt[3:0]<4'd6) ) 
        dst_mac[47:0] <= {dst_mac[39:0],frame_mux_data_fifo_dout[7:0]};     //将目的MAC地址移位寄存
    else if( (cur_state[4:0]==TRANS) & (frame_header_cnt[3:0]>=4'd2 & frame_header_cnt[3:0]<4'd8) )
        dst_mac[47:0] <= {dst_mac[39:0],8'b0};                              //将目的MAC地址输出
end

always @(posedge clk or negedge rst_n)begin
    if(!rst_n)
        src_mac[47:0] <= 48'b0;
    else if( (cur_state[4:0]==RD_HEADER) & (frame_header_cnt[3:0]>=4'd6 & frame_header_cnt[3:0]<4'd12) )
        src_mac[47:0] <= {src_mac[39:0],frame_mux_data_fifo_dout[7:0]};   
    else if( (cur_state[4:0]==TRANS) & (frame_header_cnt[3:0]>=4'd8 & frame_header_cnt[3:0]<4'd14) )
        src_mac[47:0] <= {src_mac[39:0],8'b0};
end

always @(posedge clk or negedge rst_n)begin
    if(!rst_n)
        frame_type[15:0] <= 16'b0;
    else if( (cur_state[4:0]==RD_HEADER) & (frame_header_cnt[3:0] >= 4'd12) )
        frame_type[15:0] <= {frame_type[7:0],frame_mux_data_fifo_dout[7:0]};
    else if( (cur_state[4:0]==TRANS) & (frame_header_cnt[3:0] >= 4'd14) )
        frame_type[15:0] <= {frame_type[7:0],8'b0};
end

assign frame_length[10:0] = dst_frame_len[10:0] + 10'd2;
//==========================hash lut===============================
assign hash_lut_portmap[15:0] = {12'b0,dst_portid[3:0]};
assign hash_lut_req = (cur_state[4:0]==LEARN) | (cur_state[4:0]==SEARCH);
assign hash_lut_source = cur_state[4:0]==LEARN;
assign hash_lut_mac[47:0] = (cur_state[4:0]==LEARN) ? src_mac[47:0] : dst_mac[47:0];
assign hash_lut_hash[9:0] = hash_lut_mac[9:0];

assign broadcast = (dst_mac[47:0]==48'hffff_ffff_ffff);

always @(posedge clk or negedge rst_n)begin
    if(!rst_n)
        queue_portmap[3:0] <= 4'b0;
    else if( (cur_state[4:0]==SEARCH) & hash_lut_ack)
        queue_portmap[3:0] <= hash_lut_result[3:0];
    else if( ((cur_state[4:0]==SEARCH) & hash_lut_nak) | broadcast)        //广播帧
        queue_portmap[3:0] <= ~hash_lut_portmap[3:0];
end

//===============================queue text================================
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)
        queue_sof <= 1'b0;
    else if( (cur_state[4:0]==TRANS) & (~queue_dv))
        queue_sof <= 1'b1;
    else
        queue_sof <= 1'b0;
end

always @(posedge clk or negedge rst_n)begin
    if(!rst_n)
        queue_dv <= 1'b0;
    else if(cur_state[4:0]==TRANS)  
        queue_dv <= 1'b1;
    else 
        queue_dv <= 1'b0;
end

always @(posedge clk or negedge rst_n)begin
    if(!rst_n)
        trans_mac_header_over <= 1'b0;
    else if((cur_state[4:0]==TRANS) & (frame_header_cnt[3:0]==MAC_HEADER_LEN+4'd1))
        trans_mac_header_over <= 1'b1;
    else 
        trans_mac_header_over <= 1'b0;
end


always @(posedge clk or negedge rst_n)begin
    if(!rst_n)
        queue_dout[7:0] <= 8'b0;
    else if( (cur_state[4:0]==TRANS) & (~trans_mac_header_over) )begin
        if(frame_header_cnt[3:0]==4'd0)
            queue_dout[7:0] <= {1'b0,frame_length[10:8],queue_portmap[3:0]};
        else if(frame_header_cnt[3:0]==4'd1)
            queue_dout[7:0] <= frame_length[7:0];
        else if(frame_header_cnt[3:0]<4'd8)
            queue_dout[7:0] <= dst_mac[47:40];
        else if(frame_header_cnt[3:0]<4'd14)
            queue_dout[7:0] <= src_mac[47:40];
        else
            queue_dout[7:0] <= frame_type[15:8];
    end
    else if(cur_state[4:0]==TRANS)
        queue_dout[7:0] <= frame_mux_data_fifo_dout[7:0];
end

endmodule
